Allow to pop up menus without grabbing the keyboard. Useful for stuff like
authorMichael Natterer <mitch@gimp.org>
Thu, 31 Mar 2005 17:02:19 +0000 (17:02 +0000)
committerMichael Natterer <mitch@src.gnome.org>
Thu, 31 Mar 2005 17:02:19 +0000 (17:02 +0000)
2005-03-31  Michael Natterer  <mitch@gimp.org>

Allow to pop up menus without grabbing the keyboard. Useful for
stuff like virtual keyboards. Fixes bug #159890

* gtk/gtk.symbols
* gtk/gtkmenushell.[ch]: added boolean property "take-focus"
and public API gtk_menu_shell_set/get_take_focus().

* gtk/gtkmenu.c (gtk_menu_popup)
(popup_grab_on_window): don't grab the keyboard if take_focus
is FALSE.

* gtk/gtkmenuitem.c (_gtk_menu_item_popup_submen): propagate the
parent menu_shell's take_focus property to the submenu which is
about to be popped up.

ChangeLog
ChangeLog.pre-2-10
ChangeLog.pre-2-8
gtk/gtk.symbols
gtk/gtkmenu.c
gtk/gtkmenuitem.c
gtk/gtkmenushell.c
gtk/gtkmenushell.h

index fce692ad17700502de3fe3a0ec80c25227295f90..d41547d3ac37cdcb9aa52ba287401cee42824a09 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2005-03-31  Michael Natterer  <mitch@gimp.org>
+
+       Allow to pop up menus without grabbing the keyboard. Useful for
+       stuff like virtual keyboards. Fixes bug #159890
+
+       * gtk/gtk.symbols
+       * gtk/gtkmenushell.[ch]: added boolean property "take-focus"
+       and public API gtk_menu_shell_set/get_take_focus().
+
+       * gtk/gtkmenu.c (gtk_menu_popup)
+       (popup_grab_on_window): don't grab the keyboard if take_focus
+       is FALSE.
+
+       * gtk/gtkmenuitem.c (_gtk_menu_item_popup_submen): propagate the
+       parent menu_shell's take_focus property to the submenu which is
+       about to be popped up.
+
 2005-03-30  Federico Mena Quintero  <federico@ximian.com>
 
        Merged from gtk-2-6:
index fce692ad17700502de3fe3a0ec80c25227295f90..d41547d3ac37cdcb9aa52ba287401cee42824a09 100644 (file)
@@ -1,3 +1,20 @@
+2005-03-31  Michael Natterer  <mitch@gimp.org>
+
+       Allow to pop up menus without grabbing the keyboard. Useful for
+       stuff like virtual keyboards. Fixes bug #159890
+
+       * gtk/gtk.symbols
+       * gtk/gtkmenushell.[ch]: added boolean property "take-focus"
+       and public API gtk_menu_shell_set/get_take_focus().
+
+       * gtk/gtkmenu.c (gtk_menu_popup)
+       (popup_grab_on_window): don't grab the keyboard if take_focus
+       is FALSE.
+
+       * gtk/gtkmenuitem.c (_gtk_menu_item_popup_submen): propagate the
+       parent menu_shell's take_focus property to the submenu which is
+       about to be popped up.
+
 2005-03-30  Federico Mena Quintero  <federico@ximian.com>
 
        Merged from gtk-2-6:
index fce692ad17700502de3fe3a0ec80c25227295f90..d41547d3ac37cdcb9aa52ba287401cee42824a09 100644 (file)
@@ -1,3 +1,20 @@
+2005-03-31  Michael Natterer  <mitch@gimp.org>
+
+       Allow to pop up menus without grabbing the keyboard. Useful for
+       stuff like virtual keyboards. Fixes bug #159890
+
+       * gtk/gtk.symbols
+       * gtk/gtkmenushell.[ch]: added boolean property "take-focus"
+       and public API gtk_menu_shell_set/get_take_focus().
+
+       * gtk/gtkmenu.c (gtk_menu_popup)
+       (popup_grab_on_window): don't grab the keyboard if take_focus
+       is FALSE.
+
+       * gtk/gtkmenuitem.c (_gtk_menu_item_popup_submen): propagate the
+       parent menu_shell's take_focus property to the submenu which is
+       about to be popped up.
+
 2005-03-30  Federico Mena Quintero  <federico@ximian.com>
 
        Merged from gtk-2-6:
index 05d4a5f3ba2f058b5458084dcdceff6fa5731205..4bee95dd78b2ee084d665df4f137ae29b513cd38 100644 (file)
@@ -2128,6 +2128,8 @@ gtk_menu_shell_insert
 gtk_menu_shell_prepend
 gtk_menu_shell_select_first
 gtk_menu_shell_select_item
+gtk_menu_shell_set_take_focus
+gtk_menu_shell_get_take_focus
 #endif
 #endif
 
index 31a9c360b88874c29851a8d8af9fa50e104892ba..2dfa0c22af2cee1eced26c37b874bfecc6eab074 100644 (file)
@@ -1221,7 +1221,8 @@ gtk_menu_tearoff_bg_copy (GtkMenu *menu)
 
 static gboolean
 popup_grab_on_window (GdkWindow *window,
-                     guint32    activate_time)
+                     guint32    activate_time,
+                     gboolean   grab_keyboard)
 {
   if ((gdk_pointer_grab (window, TRUE,
                         GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
@@ -1229,7 +1230,8 @@ popup_grab_on_window (GdkWindow *window,
                         GDK_POINTER_MOTION_MASK,
                         NULL, NULL, activate_time) == 0))
     {
-      if (gdk_keyboard_grab (window, TRUE,
+      if (!grab_keyboard ||
+         gdk_keyboard_grab (window, TRUE,
                             activate_time) == 0)
        return TRUE;
       else
@@ -1283,13 +1285,15 @@ gtk_menu_popup (GtkMenu             *menu,
   GtkWidget *parent;
   GdkEvent *current_event;
   GtkMenuShell *menu_shell;
-  GtkMenuPrivate *priv = gtk_menu_get_private (menu);
+  gboolean grab_keyboard;
+  GtkMenuPrivate *priv;
 
   g_return_if_fail (GTK_IS_MENU (menu));
   
   widget = GTK_WIDGET (menu);
   menu_shell = GTK_MENU_SHELL (menu);
-  
+  priv = gtk_menu_get_private (menu);
+
   menu_shell->parent_menu_shell = parent_menu_shell;
 
   priv->seen_item_enter = FALSE;
@@ -1334,9 +1338,12 @@ gtk_menu_popup (GtkMenu              *menu,
    * probably could just leave the grab on the other window, with a
    * little reorganization of the code in gtkmenu*).
    */
+  grab_keyboard = gtk_menu_shell_get_take_focus (menu_shell);
+  gtk_window_set_accept_focus (GTK_WINDOW (menu->toplevel), grab_keyboard);
+
   if (xgrab_shell && xgrab_shell != widget)
     {
-      if (popup_grab_on_window (xgrab_shell->window, activate_time))
+      if (popup_grab_on_window (xgrab_shell->window, activate_time, grab_keyboard))
        GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
     }
   else
@@ -1345,7 +1352,7 @@ gtk_menu_popup (GtkMenu               *menu,
 
       xgrab_shell = widget;
       transfer_window = menu_grab_transfer_window_get (menu);
-      if (popup_grab_on_window (transfer_window, activate_time))
+      if (popup_grab_on_window (transfer_window, activate_time, grab_keyboard))
        GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
     }
 
@@ -1437,7 +1444,7 @@ gtk_menu_popup (GtkMenu               *menu,
   gtk_widget_show (menu->toplevel);
 
   if (xgrab_shell == widget)
-    popup_grab_on_window (widget->window, activate_time); /* Should always succeed */
+    popup_grab_on_window (widget->window, activate_time, grab_keyboard); /* Should always succeed */
   gtk_grab_add (GTK_WIDGET (menu));
 }
 
index c9e5e3a6dc844b73a184a8533935ca0339924c2d..433cc410224e90fa00680359da5040c4bd09f6c9 100644 (file)
@@ -1054,13 +1054,21 @@ _gtk_menu_item_popup_submenu (GtkWidget *widget)
   menu_item->timer = 0;
 
   if (GTK_WIDGET_IS_SENSITIVE (menu_item->submenu))
-    gtk_menu_popup (GTK_MENU (menu_item->submenu),
-                   widget->parent,
-                   widget,
-                   gtk_menu_item_position_menu,
-                   menu_item,
-                   GTK_MENU_SHELL (widget->parent)->button,
-                   0);
+    {
+      gboolean take_focus;
+
+      take_focus = gtk_menu_shell_get_take_focus (GTK_MENU_SHELL (widget->parent));
+      gtk_menu_shell_set_take_focus (GTK_MENU_SHELL (menu_item->submenu),
+                                     take_focus);
+
+      gtk_menu_popup (GTK_MENU (menu_item->submenu),
+                      widget->parent,
+                      widget,
+                      gtk_menu_item_position_menu,
+                      menu_item,
+                      GTK_MENU_SHELL (widget->parent)->button,
+                      0);
+    }
 }
 
 static void
index f2986ddedea82b64100c0d9f6a824d2e47dc116f..1534bbed02774e915a5938303fed17d2b7f5c4da 100644 (file)
@@ -38,6 +38,8 @@
 #include "gtkmnemonichash.h"
 #include "gtktearoffmenuitem.h"
 #include "gtkwindow.h"
+#include "gtkprivate.h"
+#include "gtkintl.h"
 #include "gtkalias.h"
 
 #define MENU_SHELL_TIMEOUT   500
@@ -57,6 +59,11 @@ enum {
   LAST_SIGNAL
 };
 
+enum {
+  PROP_0,
+  PROP_TAKE_FOCUS
+};
+
 typedef void (*GtkMenuShellSignal1) (GtkObject           *object,
                                     GtkMenuDirectionType arg1,
                                     gpointer             data);
@@ -131,10 +138,20 @@ struct _GtkMenuShellPrivate
 {
   GtkMnemonicHash *mnemonic_hash;
   GtkKeyHash *key_hash;
+
+  gboolean take_focus;
 };
 
 static void gtk_menu_shell_class_init        (GtkMenuShellClass *klass);
 static void gtk_menu_shell_init              (GtkMenuShell      *menu_shell);
+static void gtk_menu_shell_set_property      (GObject           *object,
+                                              guint              prop_id,
+                                              const GValue      *value,
+                                              GParamSpec        *pspec);
+static void gtk_menu_shell_get_property      (GObject           *object,
+                                              guint              prop_id,
+                                              GValue            *value,
+                                              GParamSpec        *pspec);
 static void gtk_menu_shell_realize           (GtkWidget         *widget);
 static void gtk_menu_shell_finalize          (GObject           *object);
 static gint gtk_menu_shell_button_press      (GtkWidget         *widget,
@@ -230,6 +247,8 @@ gtk_menu_shell_class_init (GtkMenuShellClass *klass)
 
   parent_class = g_type_class_peek_parent (klass);
 
+  object_class->set_property = gtk_menu_shell_set_property;
+  object_class->get_property = gtk_menu_shell_get_property;
   object_class->finalize = gtk_menu_shell_finalize;
 
   widget_class->realize = gtk_menu_shell_realize;
@@ -340,6 +359,23 @@ gtk_menu_shell_class_init (GtkMenuShellClass *klass)
                                "cycle_focus", 1,
                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
 
+  /**
+   * GtkMenuShell:take-focus:
+   *
+   * A boolean that determines whether the menu and its submenus grab the
+   * keyboard focus. See gtk_menu_shell_set_take_focus() and
+   * gtk_menu_shell_get_take_focus().
+   *
+   * Since: 2.8
+   **/
+  g_object_class_install_property (object_class,
+                                   PROP_TAKE_FOCUS,
+                                   g_param_spec_boolean ("take-focus",
+                                                        P_("Take Focus"),
+                                                        P_("A boolean that determines whether the menu grabs the keyboard focus"),
+                                                        TRUE,
+                                                        GTK_PARAM_READWRITE));
+
   g_type_class_add_private (object_class, sizeof (GtkMenuShellPrivate));
 }
 
@@ -365,6 +401,45 @@ gtk_menu_shell_init (GtkMenuShell *menu_shell)
 
   priv->mnemonic_hash = NULL;
   priv->key_hash = NULL;
+  priv->take_focus = TRUE;
+}
+
+static void
+gtk_menu_shell_set_property (GObject      *object,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+  GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
+
+  switch (prop_id)
+    {
+    case PROP_TAKE_FOCUS:
+      gtk_menu_shell_set_take_focus (menu_shell, g_value_get_boolean (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+gtk_menu_shell_get_property (GObject     *object,
+                             guint        prop_id,
+                             GValue      *value,
+                             GParamSpec  *pspec)
+{
+  GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
+
+  switch (prop_id)
+    {
+    case PROP_TAKE_FOCUS:
+      g_value_set_boolean (value, gtk_menu_shell_get_take_focus (menu_shell));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
 }
 
 static void
@@ -1388,5 +1463,75 @@ _gtk_menu_shell_remove_mnemonic (GtkMenuShell *menu_shell,
   gtk_menu_shell_reset_key_hash (menu_shell);
 }
 
+/**
+ * gtk_menu_shell_get_take_focus:
+ * @menu: a #GtkMenuShell
+ *
+ * @returns: %TRUE if the menu_shell will take the keyboard focus on popup.
+ *
+ * Since: 2.8
+ **/
+gboolean
+gtk_menu_shell_get_take_focus (GtkMenuShell *menu_shell)
+{
+  GtkMenuShellPrivate *priv;
+
+  g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
+
+  priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
+
+  return priv->take_focus;
+}
+
+/**
+ * gtk_menu_shell_set_take_focus:
+ * @menu: a #GtkMenuShell
+ * @take_focus: %TRUE if the menu_shell should take the keyboard focus on popup.
+ *
+ * If @take_focus is %TRUE (the default) the menu will take the keyboard focus
+ * so that it will receive all keyboard events which is needed to enable
+ * keyboard navigation in menus.
+ *
+ * Setting @take_focus to %FALSE is useful only for special applications
+ * like virtual keyboard implementations which should not take keyboard
+ * focus.
+ *
+ * The @take_focus state of a menu or menu bar is automatically propagated
+ * to submenus whenever a submenu is popped up, so you don't have to worry
+ * about recursively setting it for your entire menu hierarchy. Only when
+ * programmatically picking a submenu and popping it up manually, the
+ * @take_focus property of the submenu needs to be set explicitely.
+ *
+ * Note that setting it to %FALSE has side-effects:
+ *
+ * If the focus is in some other app, it keeps the focus and keynav in
+ * the menu doesn't work. Consequently, keynav on the menu will only
+ * work if the focus is on some toplevel owned by the onscreen keyboard.
+ *
+ * To avoid confusing the user, menus with @take_focus set to %FALSE
+ * should not display mnemonics or accelerators, since it cannot be
+ * guaranteed that they will work.
+ *
+ * See also gdk_keyboard_grab()
+ *
+ * Since: 2.8
+ **/
+void
+gtk_menu_shell_set_take_focus (GtkMenuShell *menu_shell,
+                               gboolean      take_focus)
+{
+  GtkMenuShellPrivate *priv;
+
+  g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
+
+  priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
+
+  if (priv->take_focus != take_focus)
+    {
+      priv->take_focus = take_focus;
+      g_object_notify (G_OBJECT (menu_shell), "take-focus");
+    }
+}
+
 #define __GTK_MENU_SHELL_C__
 #include "gtkaliasdef.c"
index a0b48f4317baad6ebeb71c8789fcaf57b505df31..9a4b3a01479b4da53e093971152e9588cfbedfe5 100644 (file)
@@ -115,12 +115,16 @@ void  _gtk_menu_shell_activate         (GtkMenuShell *menu_shell);
 gint  _gtk_menu_shell_get_popup_delay  (GtkMenuShell *menu_shell);
 void  gtk_menu_shell_cancel            (GtkMenuShell *menu_shell);
 
-void _gtk_menu_shell_add_mnemonic    (GtkMenuShell *menu_shell,
-                                     guint         keyval,
-                                     GtkWidget    *target);
-void _gtk_menu_shell_remove_mnemonic (GtkMenuShell *menu_shell,
-                                     guint         keyval,
-                                     GtkWidget    *target);
+void  _gtk_menu_shell_add_mnemonic     (GtkMenuShell *menu_shell,
+                                        guint         keyval,
+                                        GtkWidget    *target);
+void  _gtk_menu_shell_remove_mnemonic  (GtkMenuShell *menu_shell,
+                                        guint         keyval,
+                                        GtkWidget    *target);
+
+gboolean gtk_menu_shell_get_take_focus (GtkMenuShell *menu_shell);
+void     gtk_menu_shell_set_take_focus (GtkMenuShell *menu_shell,
+                                        gboolean      take_focus);
 
 G_END_DECLS